﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_GL_SAMPLER_H
#define _UXS_GL_SAMPLER_H

#pragma once

#include "GLStateManager.h"
#include "GLObjectManager.h"

namespace UX_STL
{
namespace GLShell
{

	//
	// Sampler Source
	//

	struct GLSamplerSrc
	{
		struct TWrap {
			gl_wrap::type	s, t, r;
			///
			TWrap(gl_wrap::type eS = gl_wrap::REPEAT,
				  gl_wrap::type eT = gl_wrap::REPEAT,
				  gl_wrap::type eR = gl_wrap::REPEAT):
				s(eS), t(eT), r(eR) {}
			
			bool operator == (const TWrap &right) const {
				return s == right.s and t == right.t and r == right.r;
			}
		};

		struct TFilter {
			gl_filter::type	min,
							mag;
			///
			TFilter(gl_filter::type eMin = gl_filter::LINEAR_MIPMAP_LINEAR,
					gl_filter::type eMag = gl_filter::LINEAR):
				min(eMin), mag(eMag) {}

			bool operator == (const TFilter &right) const {
				return min == right.min and mag == right.mag;
			}
		};

		ivec2				vLOD;		// min, max
		float				fLodBias;	// -max_bias...max_bias
		TWrap				sWrap;
		TFilter				sFilter;
		gl_compare::type	eCompareRTT;
		uint8				uAnisotropy;
		///
		GLSamplerSrc(): vLOD(-1000,1000), fLodBias(0.0f), eCompareRTT(gl_compare::NONE), uAnisotropy(1) {}

		GLSamplerSrc(const TWrap &wrap, const TFilter &filter, gl_compare::type cmp, float bias, uint8 anisotropy, const ivec2 &lod):
			sWrap(wrap), sFilter(filter), eCompareRTT(cmp), uAnisotropy(anisotropy), vLOD(lod), fLodBias(bias) {}

		bool operator == (const GLSamplerSrc &right) const
		{
			return ( All( vLOD == right.vLOD ) and sWrap == right.sWrap and sFilter == right.sFilter and
					 eCompareRTT == right.eCompareRTT and uAnisotropy == right.uAnisotropy and
					 Equals( fLodBias, right.fLodBias ) );
		}
		
		static GLSamplerSrc Default_Bilinear_Clamp ()
		{
			return GLSamplerSrc( TWrap( gl_wrap::CLAMP, gl_wrap::CLAMP, gl_wrap::CLAMP ),
								 TFilter( gl_filter::LINEAR, gl_filter::LINEAR ),
								 gl_compare::NONE, 0.0f, 0, ivec2(-1000, 1000) );
		}
		
		static GLSamplerSrc Default_Bilinear_Repeat ()
		{
			return GLSamplerSrc( TWrap( gl_wrap::REPEAT, gl_wrap::REPEAT, gl_wrap::REPEAT ),
								 TFilter( gl_filter::LINEAR, gl_filter::LINEAR ),
								 gl_compare::NONE, 0.0f, 0, ivec2(-1000, 1000) );
		}

		static GLSamplerSrc Default_Trilinear_Clamp ()
		{
			return GLSamplerSrc( TWrap( gl_wrap::CLAMP, gl_wrap::CLAMP, gl_wrap::CLAMP ),
								 TFilter( gl_filter::LINEAR_MIPMAP_LINEAR, gl_filter::LINEAR ),
								 gl_compare::NONE, 0.0f, 0, ivec2(-1000, 1000) );
		}

		static GLSamplerSrc Default_Trilinear_Repeat ()
		{
			return GLSamplerSrc( TWrap( gl_wrap::REPEAT, gl_wrap::REPEAT, gl_wrap::REPEAT ),
								 TFilter( gl_filter::LINEAR_MIPMAP_LINEAR, gl_filter::LINEAR ),
								 gl_compare::NONE, 0.0f, 0, ivec2(-1000, 1000) );
		}

		static GLSamplerSrc Default_Anisotropy16x_Repeat ()
		{
			return GLSamplerSrc( TWrap( gl_wrap::REPEAT, gl_wrap::REPEAT, gl_wrap::REPEAT ),
								 TFilter( gl_filter::LINEAR_MIPMAP_LINEAR, gl_filter::LINEAR ),
								 gl_compare::NONE, 0.0f, 16, ivec2(-1000, 1000) );
		}
	};



	//
	// Software Sampler
	//

	class UX_EXPORT GLSamplerSW : public GLBaseObject
	{
	public:
		typedef GLSamplerSrc::TWrap		TWrap;
		typedef GLSamplerSrc::TFilter	TFilter;

	protected:
		GLSamplerSrc	_sSrc;

	public:
		GLSamplerSW() {}
		~GLSamplerSW() {}

		bool Create(const GLSamplerSrc &sSource);
		void Destroy();

		void Bind(uint8 unit)	const;
		void Bind()				const;
		void Unbind(uint8 unit)	const;
		void Unbind()			const;

		bool Apply(const GLTexture *pTex)	const;

		TWrap const &		Wrap()			const	{ return _sSrc.sWrap; }
		TFilter const &		Filter()		const	{ return _sSrc.sFilter; }
		gl_compare::type	CompareRTT()	const	{ return _sSrc.eCompareRTT; }
		uint8				Anisotropy()	const	{ return _sSrc.uAnisotropy; }
		GLSamplerSrc const&	GetSource()		const	{ return _sSrc; }
	};



	//
	// Sampler
	//

	class UX_EXPORT GLSampler : public GLBaseObject
	{
	public:
		typedef GLSamplerSrc::TWrap		TWrap;
		typedef GLSamplerSrc::TFilter	TFilter;

	protected:
		GLuint			_uID;
		GLSamplerSrc	_sSrc;

	public:
		GLSampler(): _uID(0) {}
		GLSampler(GLSampler &);
		~GLSampler();

		bool Create(const GLSamplerSrc &sSource);
		void Destroy();

		void Bind(uint8 unit)	const;
		void Bind()				const;
		void Unbind(uint8 unit)	const;
		void Unbind()			const;
		
		bool Apply(const GLTexture *pTex)	const;

		TWrap const &		Wrap()			const	{ return _sSrc.sWrap; }
		TFilter const &		Filter()		const	{ return _sSrc.sFilter; }
		gl_compare::type	CompareRTT()	const	{ return _sSrc.eCompareRTT; }
		uint8				Anisotropy()	const	{ return _sSrc.uAnisotropy; }
		GLSamplerSrc const&	GetSource()		const	{ return _sSrc; }
	};


	
//-------------------------- GLSamplerSW --------------------------//
/*
=================================================
	применить сэмплер к текстуре
=================================================
*/
	inline void GLSamplerSW::Bind(uint8 unit) const
	{
		GLenum	e_target = SMAN()->GetTexTarget( unit );
		SMAN()->ActiveTexture( unit );
		
		GL_CALL( glTexParameterf( e_target, GL_TEXTURE_LOD_BIAS, _sSrc.fLodBias ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_MIN_LOD, _sSrc.vLOD.x ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_MAX_LOD, _sSrc.vLOD.y ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_WRAP_S, _sSrc.sWrap.s ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_WRAP_T, _sSrc.sWrap.t ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_WRAP_R, _sSrc.sWrap.r ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_MIN_FILTER, _sSrc.sFilter.min ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_MAG_FILTER, _sSrc.sFilter.mag ) );
		GL_CALL( glTexParameteri( e_target, GL_TEXTURE_COMPARE_MODE, _sSrc.eCompareRTT == GL_NONE ? GL_NONE : GL_COMPARE_REF_TO_TEXTURE ) );
		
		if ( _sSrc.eCompareRTT != GL_NONE )
			GL_CALL( glTexParameteri( e_target, GL_TEXTURE_COMPARE_FUNC, _sSrc.eCompareRTT ) );
		
		if ( C_GL_EXT_texture_filter_anisotropic )
			GL_CALL( glTexParameteri( e_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, _sSrc.uAnisotropy ) );
	}

	inline void GLSamplerSW::Bind() const
	{
		Bind( SMAN()->GetCurrentLayer() );
	}

	inline void GLSamplerSW::Unbind(uint8 unit) const
	{
		// not supported
	}

	inline void GLSamplerSW::Unbind() const
	{
		// not supported
	}

/*
=================================================
	создать софтварный сэмплер
=================================================
*/
	inline bool GLSamplerSW::Create(const GLSamplerSrc &sSource)
	{
		_sSrc = sSource;

		if ( C_GL_EXT_texture_filter_anisotropic ) {
			if ( _sSrc.uAnisotropy > C_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT )
				_sSrc.uAnisotropy = C_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT;
		}
		else
			_sSrc.uAnisotropy = 0;

		return true;
	}

/*
=================================================
	удалить софтварный сэмплер
=================================================
*/
	inline void GLSamplerSW::Destroy()
	{
		_sSrc = GLSamplerSrc();
	}



//--------------------------- GLSampler ---------------------------//
/*
=================================================
	конструктор
=================================================
*/
	inline GLSampler::GLSampler(GLSampler &cSam):
		_uID(cSam._uID), _sSrc(cSam._sSrc)
	{
		cSam._uID = 0;
		cSam.Destroy();
	}

/*
=================================================
	деструктор
=================================================
*/
	inline GLSampler::~GLSampler()
	{ 
		if ( _uID != 0 )
		{
			GLOM()->DeleteSamplers( 1, &_uID );
			_uID = 0;
		}
	}

/*
=================================================
	привязать/отвязать сэмплер к текстурному слоту
=================================================
*/
	inline void GLSampler::Bind(uint8 unit) const
	{
		SMAN()->BindSampler( unit, _uID );
	}

	inline void GLSampler::Bind() const
	{
		SMAN()->BindSampler( SMAN()->GetCurrentLayer(), _uID );
	}

	inline void GLSampler::Unbind(uint8 unit) const
	{
		SMAN()->BindSampler( unit, 0 );
	}

	inline void GLSampler::Unbind() const
	{
		SMAN()->BindSampler( SMAN()->GetCurrentLayer(), 0 );
	}

/*
=================================================
	создать сэмплер
=================================================
*/
	inline bool GLSampler::Create(const GLSamplerSrc &sSource)
	{
		if ( _uID == 0 ) {
			CHECK_ERR( GLOM()->GenSamplers( 1, &_uID ) );
		}

		_sSrc = sSource;
		
		if ( C_GL_EXT_texture_filter_anisotropic ) {
			if ( _sSrc.uAnisotropy > C_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT )
				_sSrc.uAnisotropy = C_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT;
		}
		else
			_sSrc.uAnisotropy = 0;
		
		GL_CALL( glSamplerParameterf( _uID, GL_TEXTURE_LOD_BIAS, _sSrc.fLodBias ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_MIN_LOD, _sSrc.vLOD.x ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_MAX_LOD, _sSrc.vLOD.y ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_WRAP_S, _sSrc.sWrap.s ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_WRAP_T, _sSrc.sWrap.t ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_WRAP_R, _sSrc.sWrap.r ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_MIN_FILTER, _sSrc.sFilter.min ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_MAG_FILTER, _sSrc.sFilter.mag ) );
		GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_COMPARE_MODE, _sSrc.eCompareRTT == GL_NONE ? GL_NONE : GL_COMPARE_REF_TO_TEXTURE ) );
		
		if ( _sSrc.eCompareRTT != GL_NONE )
			GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_COMPARE_FUNC, _sSrc.eCompareRTT ) );

		if ( C_GL_EXT_texture_filter_anisotropic )
			GL_CALL( glSamplerParameteri( _uID, GL_TEXTURE_MAX_ANISOTROPY_EXT, _sSrc.uAnisotropy ) );

		return true;
	}

/*
=================================================
	удалить сэмплер
=================================================
*/
	inline void GLSampler::Destroy()
	{
		if ( _uID != 0 )
		{
			GLOM()->DeleteSamplers( 1, &_uID );
			_uID = 0;
		}

		_sSrc = GLSamplerSrc();
	}
	
//-------------------------------------------------------------------

}	// GLShell
}	// UX_STL

#endif	// _UXS_GL_SAMPLER_H